import { GameBlock } from "../GameBlock";
import { GameConst } from "../GameConst";
import { CatDir, GameCatData, GameCatPathData } from "../GameDefine";
import { MainConfig } from "../MainConfig";

/**
 * 游戏数据
 * @Author: lzmxqh  
 * @Date: 2022-10-23 17:35:22  
 * @Last Modified by: lzmxqh
 * @Last Modified time: 2022-10-23 23:57:20
 */
export class GameData {
    /**获取地图开始x位置 */
    public static getInitX(): number {
        return +MainConfig.width / 2 - GameConst.COL / 2 * GameConst.GAP_COL + GameConst.GAP_COL / 2;
    }

    /**获取地图开始y位置 */
    public static getInitY(): number {
        return +MainConfig.height / 2 - GameConst.ROW / 2 * GameConst.GAP_ROW + GameConst.GAP_ROW / 2;
    }

    /**获取行偏移x */
    public static getOffsetX(row: number): number {
        return (row % 2 == 0 ? -1 : 1) * GameConst.GAP_COL / 4;
    }

    /**获取地块的x位置 */
    public static getBlockX(row: number, col: number): number {
        return GameData.getInitX() + col * GameConst.GAP_COL + GameData.getOffsetX(row);
    }

    /**获取地块的y位置 */
    public static getBlockY(row: number): number {
        return GameData.getInitY() + row * GameConst.GAP_ROW;
    }

    /**获取小猫方向 */
    public static getCatDir(row: number, col: number, tRow: number, tCol: number): CatDir {
        if (tRow > row) {
            // bottom
            if (tCol > col) {
                return CatDir.BOTTOM_RIGHT;
            } else if (tCol < col) {
                return CatDir.BOTTOM_LEFT;
            }
            return row % 2 == 0 ? CatDir.BOTTOM_RIGHT : CatDir.BOTTOM_LEFT;
        } else if (tRow < row) {
            // top
            if (tCol > col) {
                return CatDir.TOP_RIGHT;
            } else if (tCol < col) {
                return CatDir.TOP_LEFT;
            }
            return row % 2 == 0 ? CatDir.TOP_RIGHT : CatDir.TOP_LEFT;
        }
        return tCol > col ? CatDir.RIGHT : CatDir.LEFT;
    }

    /**获取小猫逃跑的位置 */
    public static getCatRunPos(catData: GameCatData): GameCatData {
        if (catData.col == 0) {
            return {
                row: catData.row,
                col: catData.col - 1,
                dir: CatDir.LEFT
            };
        } else if (catData.col == GameConst.COL - 1) {
            return {
                row: catData.row,
                col: catData.col + 1,
                dir: CatDir.RIGHT
            };
        }
        return GameData.getCatPosByDir(catData);
    }

    /**获取小猫该方向下一个位置 */
    public static getCatPosByDir(catData: GameCatData): GameCatData {
        let curRow: number = catData.row;
        let curCol: number = catData.col;
        switch (catData.dir) {
            case CatDir.LEFT:
                curCol -= 1;
                break;
            case CatDir.RIGHT:
                curCol += 1;
                break;
            case CatDir.TOP_LEFT:
                curRow % 2 == 0 && (curCol -= 1);
                curRow -= 1;
                break;
            case CatDir.TOP_RIGHT:
                curRow % 2 == 1 && (curCol += 1);
                curRow -= 1;
                break;
            case CatDir.BOTTOM_LEFT:
                curRow % 2 == 0 && (curCol -= 1);
                curRow += 1;
                break;
            case CatDir.BOTTOM_RIGHT:
                curRow % 2 == 1 && (curCol += 1);
                curRow += 1;
                break;
        }
        return {
            row: curRow,
            col: curCol,
            dir: catData.dir
        };
    }

    /**
     * 获取小猫下一个位置 A*
     * @param blockMap          地图块集合
     * @param pathData          当前路径数据
     * @param waitDatas         等待检测数据
     * @param filterDatas       过滤位置集合(已走过)
     * @returns cat next pos 
     */
    public static getCatNextPos(blockMap: GameBlock[][], pathData: GameCatPathData, waitDatas: GameCatPathData[], filterDatas: GameCatData[]): GameCatPathData {
        if (!waitDatas.length) {
            // 没有等待检测的数据
            return null;
        }

        // 根据距离对等待数据进行排序
        waitDatas.sort((a, b) => {
            return GameData.getDistanceByPath(a) - GameData.getDistanceByPath(b);
        });

        // 推出第一顺位等待数据
        pathData = waitDatas.shift();

        // 将推出的等待数据压入过滤数组
        let checkData: GameCatData = pathData.curData;
        filterDatas.push(checkData);

        // 获取当前位置的周围坐标
        let aroundDatas: GameCatData[] = GameData.getAroundDatas(checkData);
        aroundDatas = aroundDatas.filter(data => {
            if (filterDatas.findIndex(filterData => GameData.isEqualPos(filterData, data)) != -1) {
                // 过滤已检测坐标
                return false;
            }

            // 过滤边缘外的坐标, 过滤不可通过地块
            return !GameData.isLimitOver(data) && GameData.isPassPos(blockMap, data);
        }).sort((a, b) => {
            // 根据距离排序
            return GameData.getLimitDistance(a) - GameData.getLimitDistance(b);
        });

        if (!aroundDatas.length) {
            // 未找到符合的下一个位置
            return GameData.getCatNextPos(blockMap, pathData, waitDatas, filterDatas);
        }

        // 是否成功找到终点
        const isSuc: boolean = aroundDatas.some(data => {
            // 当前位置数据
            const curPathData: GameCatPathData = {
                curData: data,
                distance: pathData.distance + 1,
                beforePath: pathData
            }

            const waitIndex: number = waitDatas.findIndex(waitData => GameData.isEqualPos(waitData.curData, data));
            if (waitIndex != -1) {
                // 判断当前位置和等待数据的距离
                const waitData: GameCatPathData = waitDatas[waitIndex];
                if (GameData.getDistanceByPath(curPathData) < GameData.getDistanceByPath(waitData)) {
                    // 当前位置的距离，比之前的距离短
                    waitDatas.splice(waitIndex, 1);

                    // 压入等待数组
                    waitDatas.push(curPathData);
                }
                return false;
            }

            // 压入等待数组
            waitDatas.push(curPathData);

            if (GameData.isLimit(data)) {
                // 找到终点
                pathData = curPathData;
                return true;
            }
        });

        if (isSuc) {
            return pathData;
        }
        return GameData.getCatNextPos(blockMap, pathData, waitDatas, filterDatas);
    }

    /**获取路径第一顺位位置数据 */
    public static getFirstDataByPath(pathData: GameCatPathData): GameCatData {
        while (pathData.distance > 1) {
            pathData = pathData.beforePath;
        }
        return pathData.curData;
    }

    /**获取当前路径的总体距离 */
    private static getDistanceByPath(data: GameCatPathData): number {
        return GameData.getLimitDistance(data.curData) + data.distance;
    }

    /**位置是否相等 */
    public static isEqualPos(a: GameCatData, b: GameCatData): boolean {
        return a.row == b.row && a.col == b.col;
    }

    /**获取小猫周围的坐标数据 */
    private static getAroundDatas(catData: GameCatData): GameCatData[] {
        const dirs: CatDir[] = GameConst.CAT_DIRS;
        return dirs.map(dir => {
            catData.dir = dir;
            return GameData.getCatPosByDir(catData);
        });
    }

    /**是否通过位置 */
    private static isPassPos(blockMap: GameBlock[][], catData: GameCatData): boolean {
        const block: GameBlock = blockMap[catData.row][catData.col];
        return !block.getBlockData().isClick;
    }

    /**获取到边缘的最短距离 */
    private static getLimitDistance(catData: GameCatData): number {
        return Math.min(catData.row, catData.col, Math.abs(GameConst.ROW - 1 - catData.row), Math.abs(GameConst.COL - 1 - catData.col));
    }

    /**是否超出边缘 */
    private static isLimitOver(catData: GameCatData): boolean {
        return catData.row < 0 || catData.col < 0 || catData.row > GameConst.ROW - 1 || catData.col > GameConst.COL - 1;
    }

    /**是否到边缘 */
    private static isLimit(catData: GameCatData): boolean {
        return catData.row == 0 || catData.col == 0 || catData.row == GameConst.ROW - 1 || catData.col == GameConst.COL - 1;
    }

    /**是否失败 */
    public static isFail(catData: GameCatData): boolean {
        return GameData.isLimit(catData);
    }

    /**是否胜利 */
    public static isWin(blockMap: GameBlock[][], catData: GameCatData): boolean {
        // 猫找不到下一个可行走位置则失败
        let pathData: GameCatPathData = { curData: catData, distance: 0 };
        pathData = GameData.getCatNextPos(blockMap, pathData, [pathData], [catData]);

        return !pathData
    }
}